function MessageBroker(serverMsgBrokerUrl,serverAuthUrl){
	this.sessionId = '';
	this.isConnected = false;
	this.subscriptions = [];
	this.requestId = 0;
	this.serverMsgBrokerUrl = serverMsgBrokerUrl;
	this.serverAuthUrl = serverAuthUrl;
	this.lastPingStamp = 0;
	this.isUnloading = false;
	this.authenticatedCallbackProxy = Proxy.createSimple(this,this.authenticatedCallback);
	this.processMessageProxy = Proxy.createSimple(this,this.processMessage);
	this.pollProxy = Proxy.createSimple(this,this.pollEx);
}

MessageBroker.prototype={
	authenticatedCallback:function(msg){
			if (msg.sid == null) {
				return;
			}
			this.isConnected = true;
			this.sessionId = msg.sid;
			this.poll();
	},
	authenticate:function(login, password){
		if (this.isConnected) {
			console.error('Already connected');
			return;
		}
		
		$.post(this.serverAuthUrl, getAuthenticateMsg(login, password), this.authenticatedCallbackProxy, "json");
	},
	
	getSessionId:function() {
		return this.sessionId;
	},
	
	// sendMessage is used to send a message and get
	// immediate postback from server
	sendMessage:function(msg, callback){
		if (!callback) {
			callback = this.processMessageProxy;
		}
		var request = $.post(this.serverMsgBrokerUrl + "?reqid=" + this.requestId++, msg, callback, "json");
		return request;
	},

	pollEx:function (){
			try {
				// ping the server with last message stamp
				this.sendMessage(getPingMsg(this.sessionId, this.lastPingStamp), this.processMessageProxy);
				// erase the stamp
				this.lastPingStamp = 0;
				this.isUnloading = false;
			} catch (ex) {
				console.error(ex);
			}
	},
	// polls the server for new messages
	poll:function() {
		setTimeout(this.pollProxy, 10);
	},
	
	// used to process internal messages (pings, reconnect, etc.)
	// in case the message is processed (i.e. it is internal)
	// this function will return true
	processServiceMessage:function(msg) {
		if (msg.type == 'ping') {
			this.lastPingStamp = msg.data.stamp;
			this.poll();
			return true;
		} else if (msg.type == 'reconnect') {
			return true;
		}
	},
	distributeToSubscribers:function (message) {
		var messages = (message.type == 'batch') ? message.data : [message];
		for (var mi = 0; mi < messages.length; mi++) {
			var msg = messages[mi];
			var subscription = this.subscriptions[msg.type];
			if (subscription) {
				for (var curr in subscription) {
					if (subscription.hasOwnProperty(curr)) {
						var evalStruct = subscription[curr];
						if (evalStruct.evaluator(msg.data)!= undefined) {
							for (var i = 0; i < evalStruct.callbacks.length; i++) {
								evalStruct.callbacks[i](msg.data);
							}
						}
					}
				}
			}
		}
	},
	processMessage:function (msg) {
		// we are not processing messages during unloading
		if (this.isUnloading) {
			return;
		}
		// if this message is a service we process it in this method and quit
		if (this.processServiceMessage(msg)) {
			return;
		}
		// do the processing of the subscriptions
		this.distributeToSubscribers(msg);
		// request new messages from the server
		this.poll();
	},
	
	// subscribe is used to subscribe a callback function
	// to all messages of a particular type satisfying a certain
	// criteria. Use criteria set to true to implicitly subscribe to all messages with a
	// of a certain type
	// msgType - string
	// criteria - string-based expression that can be evaluated - TBD
	// callback - handler function reference with the following (desired) notation
	//	function exampleCallback(msg){	// msg - the incoming message body
	//		return true; // important note: true here means that the event was processed by the callback
	//	}
	subscribe:function(msgType, criteria, callback) {
		var subscription = false;
		if (this.subscriptions[msgType]) {
			subscription = this.subscriptions[msgType];
		} else {
			subscription = [];
			this.subscriptions[msgType] = subscription;
		}
		var evalStruct = false;
		if (subscription[criteria]) {
			evalStruct = subscription[criteria];
		} else {
			evalStruct = { 
					evaluator : this.createEvaluator(criteria),
					callbacks : []
			};
			subscription[criteria] = evalStruct;
		}
		var exists = false;
		for (var cbi = 0; cbi < evalStruct.callbacks.length; cbi++) {
			if (evalStruct.callbacks[cbi] == callback) {
				return;
			}
		}
		evalStruct.callbacks.push(callback);
	},
	
	createEvaluator:function(criteria) {
		eval('var tmpEval = function(data) { return (data.' + criteria + ');}');
		return tmpEval;
	},
	
	// unsubscribe is used to unsubscribe a callback function
	// from all messages of a particular type satisfying a certain
	// criteria
	// msgType - string
	// criteria - string-based expression that can be evaluated
	// callback - handler function reference 
	unsubscribe:function(msgType, criteria, callback){
		var subscription = false;
		if (this.subscriptions[msgType]) {
			subscription = this.subscriptions[msgType];
		} else {
			return;
		}
		var evalStruct = false;
		if (subscription[criteria]) {
			evalStruct = subscription[criteria];
		} else {
			return;
		}
		for (var cbi = 0; cbi < evalStruct.callbacks.length; cbi++) {
			if (evalStruct.callbacks[cbi] == callback) {
				//delete evalStruct.callbacks[cbi];
				evalStruct.callbacks.splice(cbi,1);
				return;
			}
		}
	},
	disconnect:function (){
		this.isUnloading = true;
		this.isConnected = false;
	}
};

